"""
有码提取器模块 - 函数式设计
专门处理有码类型视频的识别、提取和验证

作者: Claude
创建时间: 2024-09-14
"""

import re
from typing import Optional, Tuple, Dict, Any, List
from functools import partial

try:
    from config.patterns import pattern_config
except ImportError:
    import sys
    import os
    sys.path.append(os.path.dirname(os.path.dirname(__file__)))
    from config.patterns import pattern_config


def preprocess_censored_filename(filename: str) -> str:
    """
    预处理有码文件名 - 去掉开头的数字保持格式统一
    
    :param filename: 原文件名
    :return: 预处理后的文件名
    """
    # 去掉开头的数字，如 390JNT-104 → JNT-104
    # 模式：开头的数字 + 字母系列名
    processed = re.sub(r'^\d+([A-Z]{2,6}[-_]?\d+)', r'\1', filename.upper())
    return processed


def extract_censored_id_with_pattern(filename: str, pattern: str) -> Optional[str]:
    """
    使用指定模式提取有码ID - 纯函数设计
    
    :param filename: 文件名
    :param pattern: 正则表达式模式
    :return: 提取的ID或None
    """
    try:
        # 先预处理文件名，去掉开头数字
        processed_filename = preprocess_censored_filename(filename)
        
        match = re.search(pattern, processed_filename)
        if match:
            return match.group(1)
    except Exception:
        pass
    return None


def normalize_censored_id(raw_id: str) -> str:
    """
    标准化有码ID格式
    
    :param raw_id: 原始提取的ID
    :return: 标准化的ID
    """
    if not raw_id:
        return ""
    
    normalized = raw_id.upper()
    
    # 确保字母和数字之间有连字符，并保证数字至少3位
    if '-' not in normalized and '_' not in normalized:
        # 在字母和数字之间添加连字符，数字部分至少3位（不够用0补位）
        def replace_with_padded_numbers(match):
            letters = match.group(1)
            numbers = match.group(2).lstrip('0') or '0'  # 先去掉前导0
            numbers = numbers.zfill(3)  # 然后确保至少3位
            return f"{letters}-{numbers}"
        normalized = re.sub(r'([A-Z]+)(\d+)', replace_with_padded_numbers, normalized)
    
    # 统一使用连字符而不是下划线
    normalized = normalized.replace('_', '-')
    
    # 确保已有分隔符格式中的数字至少3位
    def ensure_min_digits(match):
        letters = match.group(1)
        numbers = match.group(2).lstrip('0') or '0'  # 先去掉前导0
        numbers = numbers.zfill(3)  # 然后确保至少3位
        return f"{letters}-{numbers}"
    normalized = re.sub(r'([A-Z]+)-(\d+)', ensure_min_digits, normalized)
    
    return normalized


def is_valid_censored_id(censored_id: str) -> bool:
    """
    验证有码ID的有效性
    
    :param censored_id: 有码ID
    :return: 是否有效
    """
    if not censored_id:
        return False
    
    # 基本格式验证：字母-数字
    if not re.match(r'^[A-Z]+-\d+$', censored_id):
        return False
    
    # 分离字母和数字部分
    parts = censored_id.split('-')
    if len(parts) != 2:
        return False
    
    letters, numbers = parts
    
    # 字母部分验证（2-6个字母）
    if not (2 <= len(letters) <= 6):
        return False
    
    # 数字部分验证（通常3-4位）
    if not (1 <= len(numbers) <= 5):
        return False
    
    # 数字不能全为0
    if numbers == "0" * len(numbers):
        return False
    
    return True


def detect_censored_type(filename: str) -> Tuple[bool, Optional[str]]:
    """
    检测是否为有码类型并提取ID
    
    :param filename: 清理后的文件名
    :return: (是否为有码, 提取的ID)
    """
    censored_patterns = pattern_config.get_censored_patterns()
    
    # 按优先级尝试每个模式
    for pattern_config_item in censored_patterns:
        extracted_id = extract_censored_id_with_pattern(filename, pattern_config_item.pattern)
        
        if extracted_id:
            # 标准化ID格式
            normalized_id = normalize_censored_id(extracted_id)
            
            if is_valid_censored_id(normalized_id):
                return True, normalized_id
    
    return False, None


def extract_censored_metadata(filename: str) -> Dict[str, Any]:
    """
    提取有码视频的元数据信息
    
    :param filename: 文件名
    :return: 元数据字典
    """
    metadata = {
        'has_subtitle': False,
        'cd_number': None,
        'is_4k': False,
        'quality_info': None,
        'series_info': None
    }
    
    filename_upper = filename.upper()
    
    # 检测字幕标记
    metadata['has_subtitle'] = bool(re.search(r'[-_]C(?:\.|$)', filename_upper))
    
    # 检测4K标记
    metadata['is_4k'] = bool(re.search(r'[-_]4K', filename_upper))
    
    # 检测CD分段信息
    # 只检测明确的CD标记，避免误判主要ID中的数字
    cd_match = re.search(r'[-_]CD(\d+)', filename_upper)
    if cd_match:
        cd_num = int(cd_match.group(1))
        if 1 <= cd_num <= 20:  # 合理范围
            metadata['cd_number'] = cd_num
    
    # 检测质量信息
    quality_patterns = ['FHD', '4K', 'HD']
    for quality in quality_patterns:
        if quality in filename_upper:
            metadata['quality_info'] = quality
            break
    
    # 提取系列信息（从ID中提取字母部分）
    id_match = re.search(r'^([A-Z]+)[-_]?\d+', filename_upper)
    if id_match:
        metadata['series_info'] = id_match.group(1)
    
    return metadata


def standardize_censored_filename(censored_id: str, metadata: Dict[str, Any]) -> str:
    """
    标准化有码文件名
    
    :param censored_id: 有码ID
    :param metadata: 元数据信息
    :return: 标准化的文件名（不含扩展名）
    """
    # 基础格式：已标准化的ID
    base_name = censored_id
    
    # 添加后缀
    suffix_parts = []
    
    # 4K标记
    if metadata.get('is_4k', False):
        suffix_parts.append('4K')
    
    # CD分段标记
    if metadata.get('cd_number'):
        suffix_parts.append(f"CD{metadata['cd_number']}")
    
    # 字幕标记
    if metadata.get('has_subtitle', False):
        suffix_parts.append('C')
    
    # 组合最终文件名
    if suffix_parts:
        final_name = f"{base_name}-{'-'.join(suffix_parts)}"
    else:
        final_name = base_name
    
    return final_name.upper()


def validate_censored_result(standardized_name: str) -> bool:
    """
    验证有码标准化结果
    
    :param standardized_name: 标准化后的文件名
    :return: 验证是否通过
    """
    # 基础格式验证：字母-数字
    base_pattern = r'^[A-Z]+-\d+'
    
    if not re.match(base_pattern, standardized_name):
        return False
    
    # 完整格式验证（包括可选后缀）
    full_pattern = r'^[A-Z]+-\d+(?:-(?:4K|CD\d+|C))*$'
    
    return bool(re.match(full_pattern, standardized_name))


def get_series_priority(series_name: str) -> int:
    """
    获取系列优先级 - 用于特殊系列的优先识别
    
    :param series_name: 系列名称
    :return: 优先级分数
    """
    high_priority_series = {
        'SSIS': 100,
        'SONE': 100,  
        'SKYHD': 90,
        'SIRO': 90,
        'FSDSS': 85,
        'WAAA': 80,
        'ATID': 80,
        'HMN': 80
    }
    
    return high_priority_series.get(series_name, 0)


def process_censored_filename(original_filename: str, cleaned_filename: str) -> Optional[Tuple[str, Dict[str, Any]]]:
    """
    处理有码文件名的主函数 - 完整流水线
    
    :param original_filename: 原始文件名
    :param cleaned_filename: 清理后的文件名
    :return: (标准化文件名, 元数据) 或 None
    """
    # 步骤1: 检测是否为有码类型
    is_censored, censored_id = detect_censored_type(cleaned_filename)
    
    if not is_censored or not censored_id:
        return None
    
    # 步骤2: 提取元数据
    metadata = extract_censored_metadata(cleaned_filename)
    metadata['video_type'] = 'CENSORED'
    metadata['extracted_id'] = censored_id
    metadata['original_filename'] = original_filename
    
    # 步骤3: 添加系列优先级信息
    if metadata['series_info']:
        metadata['series_priority'] = get_series_priority(metadata['series_info'])
    
    # 步骤4: 标准化文件名
    standardized_name = standardize_censored_filename(censored_id, metadata)
    
    # 步骤5: 验证结果
    if not validate_censored_result(standardized_name):
        return None
    
    metadata['is_valid'] = True
    
    return standardized_name, metadata


def batch_process_censored_filenames(filename_list: List[Tuple[str, str]]) -> List[Tuple[str, Optional[str], Optional[Dict[str, Any]]]]:
    """
    批量处理有码文件名
    
    :param filename_list: [(原始文件名, 清理后文件名), ...]
    :return: [(原始文件名, 标准化文件名或None, 元数据或None), ...]
    """
    def process_single(filename_pair: Tuple[str, str]) -> Tuple[str, Optional[str], Optional[Dict[str, Any]]]:
        """处理单个文件名的映射函数"""
        original, cleaned = filename_pair
        result = process_censored_filename(original, cleaned)
        
        if result:
            standardized, metadata = result
            return original, standardized, metadata
        else:
            return original, None, None
    
    # 使用函数式编程的map进行批量处理
    return list(map(process_single, filename_list))


def filter_by_series(results: List[Tuple[str, Optional[str], Optional[Dict[str, Any]]]], 
                    target_series: str) -> List[Tuple[str, Optional[str], Optional[Dict[str, Any]]]]:
    """
    按系列筛选结果 - 函数式筛选
    
    :param results: 处理结果列表
    :param target_series: 目标系列名称
    :return: 筛选后的结果
    """
    def is_target_series(result_tuple: Tuple[str, Optional[str], Optional[Dict[str, Any]]]) -> bool:
        """筛选函数"""
        _, _, metadata = result_tuple
        if metadata and metadata.get('series_info'):
            return metadata['series_info'].upper() == target_series.upper()
        return False
    
    return list(filter(is_target_series, results))


# 偏函数工厂 - 提供便捷的配置化接口
def create_censored_extractor_with_series_filter(allowed_series: List[str]) -> callable:
    """
    创建带系列筛选的有码提取器
    
    :param allowed_series: 允许的系列列表
    :return: 配置好的提取器函数
    """
    def filtered_extractor(original_filename: str, cleaned_filename: str) -> Optional[Tuple[str, Dict[str, Any]]]:
        result = process_censored_filename(original_filename, cleaned_filename)
        if result:
            standardized, metadata = result
            series = metadata.get('series_info', '')
            if series.upper() in [s.upper() for s in allowed_series]:
                return result
        return None
    
    return filtered_extractor


# 导出的函数式接口
__all__ = [
    "detect_censored_type",
    "process_censored_filename", 
    "batch_process_censored_filenames",
    "validate_censored_result",
    "filter_by_series",
    "create_censored_extractor_with_series_filter"
]